﻿using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Text;

namespace SimpleMapper
{
    /// <summary>
    /// 对象转换程序
    /// </summary>
    internal sealed class ObjectConverter
    {
        /// <summary>
        /// 更改类型泛型方法元数据信息
        /// </summary>
        private static MethodInfo changeGenericType = null;
        /// <summary>
        /// 更改类型方法元数据信息映射
        /// </summary>
        private static readonly ConcurrentDictionary<Type, MethodInfo> changeTypeMap = new ConcurrentDictionary<Type, MethodInfo>();
        /// <summary>
        /// 给定类型，获取更改类型方法元数据信息
        /// </summary>
        /// <param name="dstType"></param>
        /// <returns></returns>
        public static MethodInfo GetChangeTypeMethodInfo(Type dstType)
        {
            if (changeGenericType == null)
            {
                lock (changeTypeMap)
                {
                    if (changeGenericType == null)
                    {
                        var selfType = typeof(ObjectConverter);
                        changeGenericType = selfType.GetMethod("ChangeType", new[] { typeof(object) });
                        if (changeGenericType == null ||
                            !changeGenericType.IsGenericMethodDefinition)
                            throw new Exception("ChangeType不存在或不受支持");
                    }
                }
            }
            return changeTypeMap.GetOrAdd(dstType, changeGenericType.MakeGenericMethod(dstType));
        }
        /// <summary>
        /// 转换给定数据<paramref name="srcValue"/>的类型为<typeparamref name="TDestination"/>
        /// </summary>
        /// <typeparam name="TDestination">目标类型</typeparam>
        /// <param name="srcValue"></param>
        /// <returns></returns>
        public static TDestination ChangeType<TDestination>(object srcValue)
        {
            // A01 如果srcValue为空引用，则返回指定类型的默认值
            if (srcValue == null) return default;
            // A02 如果srcValue可以转换为指定类型
            // A02 表示srcValue的类型与指定目标类型相符
            // A02 则返回转换后的目标值
            if (srcValue is TDestination dstValue) return dstValue;
            // A03 开始取得源类型 与 目标类型
            var dstType = typeof(TDestination);
            var srcType = srcValue.GetType();
            // A04 目标类型为值类型
            if (dstType.IsValueType)
            {
                // B01 目标值类型 为 枚举类型
                if (dstType.IsEnum) return ConvertToEnumValueType<TDestination>(srcValue, srcType, dstType);
                // B02 目标值类型 为 基本数据类型
                if (dstType.IsBasicType()) return ConvertToBasicDataType<TDestination>(srcValue, srcType, dstType);
                // B03 目标值类型 为 泛型值类型（泛型结构体 与 可为空类型）
                if (dstType.IsGenericType) return ConvertToGenericValueType<TDestination>(srcValue, srcType, dstType);
                // B04 其它结构体
                return default;
            }
            else
            {
                // C01 目标引用类型 为 字符串类型
                if (dstType.IsStringType() && srcValue.ToString() is TDestination stringValue) return stringValue;

                // C02 DBNull.Value也按引用类型null

                // C03 目标引用类型 为 其它引用类型
                // C03 在 A02 处已经进行过类型检查，如果相符已返回
                // C03 执行到此说明srcValue与T的类型不符，所以返回目标引用类型默认值 
                return default;
            }
        }
        /// <summary>
        /// 转换为枚举值类型
        /// </summary>
        /// <typeparam name="TDestination"></typeparam>
        /// <param name="srcValue"></param>
        /// <param name="srcType"></param>
        /// <param name="dstType"></param>
        /// <returns></returns>
        private static TDestination ConvertToEnumValueType<TDestination>(object srcValue, Type srcType, Type dstType)
        {
            // 如果源类型为基元类型 或 字符串类型
            if (srcType.IsPrimitive || srcType == typeof(string))
            {
                // 检查源值 是否在 目标类型（枚举）中定义
                if (Enum.IsDefined(dstType, srcValue))
                {
                    // 如果定义 则解析源值字符串为目标枚举
                    return (TDestination)Enum.Parse(dstType, srcValue.ToString(), true);
                }
            }
            // 否则返回枚举默认值
            return default;
        }
        /// <summary>
        /// 转换为基本数据类型
        /// </summary>
        /// <typeparam name="TDestination"></typeparam>
        /// <param name="srcValue"></param>
        /// <param name="srcType"></param>
        /// <param name="dstType"></param>
        /// <returns></returns>
        private static TDestination ConvertToBasicDataType<TDestination>(object srcValue, Type srcType, Type dstType)
        {
            // 源类型 为 基本数据 则直接转换后返回
            if (srcType.IsBasicType()) return (TDestination)Convert.ChangeType(srcValue, dstType);
            // 否则返回目标类型的默认值
            return default;
        }
        /// <summary>
        /// 转换为泛型的值类型
        /// </summary>
        /// <typeparam name="TDestination"></typeparam>
        /// <param name="srcValue"></param>
        /// <param name="srcType"></param>
        /// <param name="dstType"></param>
        /// <returns></returns>
        private static TDestination ConvertToGenericValueType<TDestination>(object srcValue, Type srcType, Type dstType)
        {
            // 获取可能存在的基础类型
            var nullableUnderlyingType = Nullable.GetUnderlyingType(dstType);
            // 不存在表示不是可为空值类型，返回默认值
            if (nullableUnderlyingType == null) return default;
            // 基础类型为枚举，则转换为枚举类型（只要基础类型一致，可为空可以强制转换）
            if (nullableUnderlyingType.IsEnum) return ConvertToEnumValueType<TDestination>(srcValue, srcType, nullableUnderlyingType);
            // 基础类型为基元类型，则转换为基元类型（只要基础类型一致，可为空可以强制转换）
            if (nullableUnderlyingType.IsBasicType()) return ConvertToBasicDataType<TDestination>(srcValue, srcType, nullableUnderlyingType);
            // 其它例如结构体可为空，直接返回默认值
            return default;
        }

    }
}
